#!/bin/bash
# 测试套公共函数
# 约定：
#   1、以下划线"_"开头的函数和变量用例不能直接调用
#   2、环境变量全大写，全局变量加上"g_"前置，局部变量统一加"local"修饰

# source自己定义的其他公共函数
source "${TST_TS_TOPDIR}/tst_lib/other_common.sh" || exit 1

ts_setup() {
    msg "this is ts_setup"
    return 0
}

tc_setup_common() {
    msg "this is tc_setup_common"
    return 0
}

tc_teardown_common() {
    msg "this is tc_teardown_common"
    return 0
}

ts_teardown() {
    msg "this is ts_teardown"
    return 0
}

# 获取系统当前的tkernel发布类型
get_tk_release_type() {
    local tk_version
    uname -r | grep "^5\.4\." >/dev/null && tk_version=tk4
    uname -r | grep "^4\.14\." >/dev/null && tk_version=tk3

    local tk_release=public
    uname -r | grep tlinux >/dev/null && tk_release=private

    local tk_prefix="${tk_version}-${tk_release}"
    uname -r | grep "^5\.1[78]\." >/dev/null && tk_prefix=ocks

    local tk_arch
    tk_arch="$(uname -m)"

    echo "${tk_prefix}-${tk_arch}"
}

# 将kconfig格式化，去掉空行和未开启的项
# $1 -- kconfig file
format_kconfig() {
    grep -v "^[[:blank:]]*#" "$1" | grep -v "^[[:blank:]]*$" | sort
}

# 获取当前系统的发布类型
get_ts_release_type() {
    source /etc/os-release

    local ts_type
    if [ "$NAME" == "TencentOS Server" ] || [ "$NAME" == "Tencent tlinux" ] || [ "$NAME" == "TencentOS" ] ; then
        ts_type=ts
    elif [ "$NAME" == "TencentOS Server Stream" ]; then
        ts_type=tss
    elif [ "$NAME" == "OpenCloudOS Stream" ]; then
        ts_type=ocs
    elif [ "$NAME" == "OpenCloudOS" ]; then
        ts_type=oc
    else
        cat /etc/os-release
        err "unknown ts type"
        return 1
    fi

    local ts_version="$VERSION_ID"
    if [ -z "$ts_version" ]; then
        cat /etc/os-release
        err "unknown ts version"
        return 1
    fi

    echo "${ts_type}-${ts_version}-$(uname -m)"
}

# 获取二进制包对应的源码包
# Args:
#   $1 -- binary package name
get_src_pkg_by_bin(){
    local bin_pkg="$1"
    local src_pkg=""
    [ -z "$1" ] && return 1

    src_pkg="$(rpm -qi "$bin_pkg" 2>/dev/null | grep -i "Source RPM[[:blank:]]*:" | head -n 1 | awk '{print $NF}')"
    if [ -n "$src_pkg" ]; then
        echo -n "$src_pkg"
        return
    fi
    src_pkg="$(yum info "$bin_pkg" 2>/dev/null | grep -i "Source[[:blank:]]*:" | head -n 1 | awk '{print $NF}')"
    echo "$src_pkg"
}

# 默认不需要测试scl包，因此将其disable
disable_scl_repo() {
    for f in $(rpm -qa | grep -w release | grep -w scl | xargs rpm -ql 2>/dev/null | grep "^/etc/yum.repos.d/"); do
        sed -i "s|enabled=1|enabled=0|g" "$f"
    done
}

# 有的软件包会依赖scl中的包，在安装阶段需要将其打开
enable_scl_repo() {
    for f in $(rpm -qa | grep -w release | grep -w scl | xargs rpm -ql 2>/dev/null | grep "^/etc/yum.repos.d/"); do
        sed -i "s|enabled=0|enabled=1|g" "$f"
    done
}

# 对epel相关的repo做disable或enable操作
# $1 -- enable or disable
epel_repo_switch() {
    if [ $# -ne 1 ]; then
        echo "Usage: $0 <disable|enable>"
        exit 1
    fi

    local action=$1

    if [ "$action" != "disable" ] && [ "$action" != "enable" ]; then
        echo "Invalid action. Use 'disable' or 'enable'."
        exit 1
    fi
    epel_repos_files=$(grep -l -i -w "\[epel\]" /etc/yum.repos.d/* 2>/dev/null)
    for file in $epel_repos_files; do
        if [ "$action" == "disable" ]; then
            sed -i 's/enabled=1/enabled=0/g' $file
        elif [ "$action" == "enable" ]; then
            sed -i 's/enabled=0/enabled=1/g' $file
        fi
    done
}

# # 关闭不在白名单中的 repo
# # $1 -- 允许处于enable状态的repo
# disable_non_whitelisted_repos() {
#     local whitelist="$1"
#     local all_repo_files
#     local non_whitelisted_files
#     echo "$whitelist" > whitelist.txt

#     # 获取 /etc/yum.repos.d/ 下的所有 .repo 文件
#     all_repo_files=$(find /etc/yum.repos.d/ -type f -name "*.repo")

#     # 获取不在白名单中的 .repo 文件
#     non_whitelisted_files=$(echo "$all_repo_files" | grep -v -F -f whitelist.txt)

#     rm -rf whitelist.txt

#     # 关闭不在白名单中的 repo
#     for file in $non_whitelisted_files; do
#         sed -i 's/enabled=1/enabled=0/g' "$file"
#     done
# }

# 将需要测试的软件包列表进行处理，去掉不需要测试的包
# $1 -- 要处理的包列表
# $2 -- 处理完成后的列表文件
# $3 -- 要测试的repo源，可选
# 注意，处理后的包文件列表有两列，第一列是短的包名，第二列是长的包名
clean_package_list_to_test() {
    local file_in="$1"
    local file_out="$2"
    local repo_enable="$3"
    [ -n "$repo_enable" ] && repo_enable="--enablerepo=$repo_enable"
    msg "CLEAN: clean_package_list_to_test $*"
    if [ ! -f "$file_in" ]; then
        msg "fail $file_in not exist"
        return 1
    fi
    msg "CLEAN: file $file_in has $(wc -l "$file_in") lines total"
    rm -rfv "$file_out"
    touch "$file_out"
    grep -v "^Installed Packages\|^Available Packages" "$file_in" | awk '{print $1}' |
        sed "s|.*/||g" | sed "s|\.rpm$||g" | sort | uniq >"$file_out".tmp
    msg "CLEAN: file $file_in has $(wc -l "$file_out".tmp) valid lines"

    msg "CLEAN: the ix86 no need test, remove them $(grep -c "\.i[0-9]86$" "$file_out".tmp):"
    grep "\.i[0-9]86$" "$file_out".tmp
    sed -i "/\.i[0-9]86$/d" "$file_out".tmp

    msg "only the available packages need test"
    local nr_packages
    nr_packages=$(wc -l <"$file_out".tmp)
    if [ "$nr_packages" -eq 0 ]; then
        msg "no package need test"
        cp "$file_out".tmp "$file_out"
        return
    fi
    # 每次yum list 10000个包，能list出来的才进行测试
    nr_loop=$((nr_packages / 10000 + 1))
    for _ in $(seq $nr_loop); do
        # shellcheck disable=SC2086
        # shellcheck disable=SC2046
        yum $repo_enable list available $(sed -n "1,10000 p" "$file_out".tmp) |
            sed "1,/Available Packages/d" | sed ':a;N;s/\n[[:blank:]]/ /g;b a' |
            while read -r p v _; do
                echo "${p} ${p%.*}-${v#*:}.${p##*.}" >>"$file_out"
            done
        sed -i "1,10000 d" "$file_out".tmp
    done
    # 去掉release包的测试，这些包会变更repo源的内容
    msg "CLEAN: the release package no need test $(grep -c "\-release-" "$file_out"):"
    grep "\-release-" "$file_out"
    sed -i "/-release-/d" "$file_out"

    msg "CLEAN: the package list file $file_out after clean has $(wc -l "$file_out") lines"
}

# 测试module包安装
# $1 -- 测试用的临时目录
# $2 -- 要安装的软件包列表，yum list输出的即可
test_dnf_module_install() {
    msg "test_dnf_module_install $*"
    mkdir -p "$1"

    local cwd
    cwd="$(pwd)"
    local tmpdir
    tmpdir="$(realpath "$1")"
    local pkg_list="$2"
    local test_flag_dmi=0
    local nr_list
    local arch
    arch="$(uname -m)"
    nr_list="$(wc -l <"${pkg_list}")"
    if [ "$nr_list" -eq 0 ]; then
        msg "no package to test"
        return 0
    fi

    local install_index=0
    local log_file

    mkdir -p "$tmpdir/workdir"
    cd "$tmpdir/workdir" || return 1
    # 获取已安装的module
    dnf module list --installed >"${tmpdir}/installed.list"

    while read -r line; do
        install_index=$((install_index + 1))
        msg "${install_index} / ${nr_list} : $line"

        local package_name
        package_name="$(echo "$line")"
        [ -z "$package_name" ] && continue

        # 检查软件包是否安装成功
        log_file="$tmpdir/log.${package_name}"
        module_pkg=$(dnf module provides ${package_name} |grep "^Module" | uniq | awk '{print $3}' | head -n 1)
        if [ -z $module_pkg ]; then
            msg "${package_name} not enable"
            continue
        fi
        msg "$module_pkg need enable!"
        # enable该module包所需主包的版本
        if dnf module enable -y $module_pkg; then
            msg "$module_pkg enable success"
            if dnf install -y "$package_name" >"${log_file}.install" 2>&1; then
                msg "yum install module package $package_name success"
            else
                msg "yum install module package $package_name fail"
                test_flag_dmi=$((test_flag_dmi + 1))
                cat "${log_file}.install"
                continue
            fi
        else
            # 如果enable主包失败，需要先将当前主包reset，再enable所需的版本
            module_pkg_main=$(echo ${module_pkg%%:*})
            msg "$module_pkg_main need to reset!"
            if dnf module reset -y $module_pkg_main; then
                if dnf module enable -y $module_pkg; then
                    if dnf install -y "$package_name" >"${log_file}.install" 2>&1; then
                        msg "yum install module package $package_name success"
                    else
                        msg "yum install module package $package_name fail"
                        test_flag_dmi=$((test_flag_dmi + 1))
                        cat "${log_file}.install"
                        continue
                    fi                   
                else
                    msg "dnf module enable $module_pkg fail"
                    msg "yum install module package $package_name fail"
                    continue
                fi
            else
                msg "dnf module reset -y $module_pkg_main fail"
                msg "yum install module package $package_name fail"
                continue
            fi
        fi

        # 如果软件包原来就装了，本用例不remove
        if grep -q "^${package_name}[[:blank:]]" "${tmpdir}/installed.list" ||
            grep -q "^${package_name}\.\(${arch}\|noarch\)[[:blank:]]" "${tmpdir}/installed.list"; then
            # 软件包原本就已安装，不移除
            msg "package $package_name don't remove"
        else
            # 软件包是本用例测试时安装的，需要移除
            if dnf remove -y "$package_name">"${log_file}.remove" 2>&1; then
                msg "dnf module remove $package_name success"
            else
                if grep -iqw "The operation would result in removing the following protected packages" "${log_file}.remove"; then
                    msg "package $package_name is protected, cann't be removed"
                else
                    msg "dnf module remove $package_name fail"
                    test_flag_dmi=$((test_flag_dmi + 1))
                    cat "${log_file}.remove"
                fi
            fi
        fi
    done <"${pkg_list}"

    cd "$cwd" || return 1
    msg "test_flag_dmi=$test_flag_dmi"
    if [ $test_flag_dmi -eq 0 ]; then
        return 0
    else
        return 1
    fi
}

# 测试单个软件包是否安装成功
# $1 -- repo
# $2 -- 待安装的软件包名
# $3 -- 安装日志
test_single_rpm_install() {
    # shellcheck disable=SC2086
    if yum $1 install -y "$2" >"$3.install" 2>&1; then
        if ! grep "scriptlet failed" "$3.install"; then
            return 0
        fi
    else
        if grep "conflicts with" "$3.install"; then
            return 0
        fi
    fi
    cat "$3.install"
    return 1
}

# 测试rpm包的移除
# $1 -- 短包名，例如gcc.x86_64
# $2 -- 长包名，例如gcc-8.5.0-10.1.tl3.x86_64
# $3 -- 移除日志
# $4 -- 系统已安装的包列表，yum list installed输出的即可
test_single_rpm_remove() {
    local pkg_short="$1"
    local pkg_long="$2"
    local log_file="$3"
    local installed_list="$4"
    # 如果包之前已经安装，这里就不remove了
    if grep "^${pkg_short}[[:blank:]]" "${installed_list}"; then
        msg "the package $pkg_short no need remove"
        return 0
    fi
    if yum remove -y "$pkg_long" >"${log_file}.remove" 2>&1; then
        if grep "scriptlet failed" "${log_file}.remove"; then
            cat "${log_file}.remove"
            return 1
        else
            msg "the package $pkg_short remove success"
            return 0
        fi
    fi
    if grep -iqw "The operation would result in removing the following protected packages" "${log_file}.remove"; then
        msg "package $pkg_short is protected, can't be removed"
        return 0
    fi
    cat "${log_file}.remove"
    return 1
}

# 测试软件包安装
# $1 -- 测试用的临时目录
# $2 -- 要安装的软件包列表，yum list输出的即可
# $3 -- 同yum的--repo，可选参数，有的软件在指定repo中
test_yum_install() {
    msg "test_yum_install $*"
    mkdir -p "$1"

    local cwd
    cwd="$(pwd)"
    local tmpdir
    tmpdir="$(realpath "$1")"
    local pkg_list="$2"
    local repo="$3"
    [ -n "$repo" ] && repo="--enablerepo=$repo"

    clean_package_list_to_test "$pkg_list" "${tmpdir}/test.list" "$3"
    local test_flag_yi=0
    local nr_list
    local arch
    arch="$(uname -m)"
    nr_list="$(wc -l <"${tmpdir}/test.list")"
    # shellcheck disable=SC2086
    yum $repo list installed >"${tmpdir}/installed.list"

    if [ "$nr_list" -eq 0 ]; then
        msg "no package to test"
        return 0
    fi

    local install_index=0
    local log_file

    mkdir -p "$tmpdir/workdir"
    cd "$tmpdir/workdir" || return 1

    while read -r pkg_short pkg_long; do
        install_index=$((install_index + 1))
        msg "${install_index} / ${nr_list} : $pkg_short $pkg_long"

        [ -z "$pkg_long" ] && continue

        # 检查软件包是否安装成功
        log_file="$tmpdir/log.${pkg_long}"
        if test_single_rpm_install "$repo" "$pkg_long" "$log_file"; then
            msg "yum install $pkg_long success"
        else
            msg "yum install $pkg_long fail"
            test_flag_yi=$((test_flag_yi + 1))
            continue
        fi

        # 检查软件包是否移除成功
        if test_single_rpm_remove "$pkg_short" "$pkg_long" "$log_file" "${tmpdir}/installed.list"; then
            msg "yum remove $package_name success"
        else
            msg "yum remove $package_name fail"
            test_flag_yi=$((test_flag_yi + 1))
        fi
    done <"${tmpdir}/test.list"

    cd "$cwd" || return 1
    msg "test_flag_yi=$test_flag_yi"
    if [ $test_flag_yi -eq 0 ]; then
        return 0
    else
        return 1
    fi
}

# 测试软件包安装(仅安装，不移除)
# $1 -- 测试用的临时目录
# $2 -- 要安装的软件包列表，yum list输出的即可
# $3 -- 同yum的--repo，可选参数，有的软件在指定repo中
test_yum_only_install() {
    msg "test_yum_only_install $*"
    mkdir -p "$1"

    local cwd
    cwd="$(pwd)"
    local tmpdir
    tmpdir="$(realpath "$1")"
    local pkg_list="$2"
    local repo="$3"
    [ -n "$repo" ] && repo="--enablerepo=$repo"

    clean_package_list_to_test "$pkg_list" "${tmpdir}/test.list" "$3"
    local test_flag_yi=0
    local nr_list
    local arch
    arch="$(uname -m)"
    nr_list="$(wc -l <"${tmpdir}/test.list")"
    # shellcheck disable=SC2086
    yum $repo list installed >"${tmpdir}/installed.list"

    if [ "$nr_list" -eq 0 ]; then
        msg "no package to test"
        return 0
    fi

    local install_index=0
    local log_file

    mkdir -p "$tmpdir/workdir"
    cd "$tmpdir/workdir" || return 1

    while read -r pkg_short pkg_long; do
        install_index=$((install_index + 1))
        msg "${install_index} / ${nr_list} : $pkg_short $pkg_long"

        [ -z "$pkg_long" ] && continue

        # 检查软件包是否安装成功
        log_file="$tmpdir/log.${pkg_long}"
        if test_single_rpm_install "$repo" "$pkg_long" "$log_file"; then
            msg "yum install $pkg_long success"
        else
            msg "yum install $pkg_long fail"
            test_flag_yi=$((test_flag_yi + 1))
            continue
        fi
    done <"${tmpdir}/test.list"

    cd "$cwd" || return 1
    msg "test_flag_yi=$test_flag_yi"
    if [ $test_flag_yi -eq 0 ]; then
        return 0
    else
        return 1
    fi
}

# 获取文件列表里面可执行elf文件
# $1 -- 输入文件列表
# $2 -- 输出文件列表
get_elf_executable() {
    local input_file="$1"
    local output_file="$2"
    local nr_input
    local cmd
    nr_input="$(wc -l <"$input_file")"

    msg "total $nr_input input file"
    xargs file <"$input_file" >"$input_file".file.tmp
    grep "ELF " "$input_file".file.tmp | awk -F : '{print $1}' >"$input_file".file.cmd
    while read -r line; do
        if [ -x "$line" ]; then
            echo $line >> $output_file
        fi
    done < "$input_file".file.cmd
    msg "get elf executable file $(wc -l <"$output_file")"
    rm -rf "$input_file".file.tmp
    rm -rf "$input_file".file.cmd
}

# 测试命令的执行
# $1 -- 测试用的临时目录
# $2 -- 要执行的命令列表
test_command_execute() {
    msg "test_command_execute $*"
    mkdir -p "$1"

    local cwd
    cwd="$(pwd)"
    local tmpdir
    tmpdir="$(realpath "$1")"
    local command_list
    command_list="$(realpath "$2")"
    local nr_command
    nr_command="$(wc -l <"$command_list")"

    if [ "$nr_command" -eq 0 ]; then
        msg "no command to test"
        return 0
    fi

    local index=0
    local test_flag_ce=0
    local test_command

    mkdir -p "$tmpdir/workdir"
    cd "$tmpdir/workdir" || return 1

    while [ $index -lt "$nr_command" ]; do
        index=$((index + 1))
        test_command="$(sed -n "${index}p" "$command_list")"
        msg "$index / $nr_command : $(ls --full-time -l "$test_command")"
        [ -z "$test_command" ] && continue

        # 设置core dump路径，用来判断命令是否生成了core文件
        echo "$tmpdir/${index}.core" >/proc/sys/kernel/core_pattern

        # 先尝试执行命令，成功则通过
        if timeout -s SIGKILL 1 "$test_command" --help >"$tmpdir/${index}.log" 2>&1; then
            # 如果有core文件生成，则表示执行失败
            if [ "$(find "$tmpdir" -name "${index}.core*" | wc -l)" -ne 0 ]; then
                msg "AAA $index command: $test_command core dump, execute fail"
                test_flag_ce=$((test_flag_ce + 1))
            else
                msg "BBB $index command: $test_command execute success"
            fi
        elif timeout -s SIGKILL 1 "$test_command" --version >>"$tmpdir/${index}.log" 2>&1; then
            # 如果有core文件生成，则表示执行失败
            if [ "$(find "$tmpdir" -name "${index}.core*" | wc -l)" -ne 0 ]; then
                msg "CCC $index command: $test_command core dump, execute fail"
                test_flag_ce=$((test_flag_ce + 1))
            else
                msg "DDD $index command: $test_command execute success"
            fi
        # 如果有core文件生成，则表示执行失败
        elif [ "$(find "$tmpdir" -name "${index}.core*" | wc -l)" -ne 0 ]; then
            msg "EEE $index command: $test_command core dump, execute fail"
            test_flag_ce=$((test_flag_ce + 1))
        # 如果执行时缺库，则表示执行失败
        elif grep "error while loading shared libraries" "$tmpdir/${index}.log"; then
            msg "FFF $index command: $test_command load library fail"
            cat "$tmpdir/${index}.help"
            test_flag_ce=$((test_flag_ce + 1))
        # 有的软链接对应的文件不存在
        elif ! realpath "$test_command" >/dev/null 2>&1; then
            msg "GGG $index command: $test_command realpath fail"
            test_flag_ce=$((test_flag_ce + 1))
        # elf文件如果可以正常exec，则表示执行成功
        elif file "$(realpath "$test_command")" 2>&1 | grep -qw ELF; then
            if timeout -s SIGKILL 1 strace "$test_command" --help 2>&1 | head -n 1 | grep -q "^exec.* = 0$"; then
                msg "HHH $index command: $test_command execute success"
            else
                msg "III $index command: $test_command execute fail"
                test_flag_ce=$((test_flag_ce + 1))
            fi
        elif timeout -s SIGKILL 1 "$test_command" >>"$tmpdir/${index}.log" 2>&1; then
            # 如果有core文件生成，则表示执行失败
            if [ "$(find "$tmpdir" -name "${index}.core*" | wc -l)" -ne 0 ]; then
                msg "JJJ $index command: $test_command core dump, execute fail"
                test_flag_ce=$((test_flag_ce + 1))
                continue
            else
                msg "KKK $index command: $test_command execute success"
            fi
        # 如果正常输出打印信息，则表示执行成功
        elif grep -iqw "invalid\|argument\|Unknown\|option\|Usage" "$tmpdir/${index}.log"; then
            msg "LLL $index command: $test_command execute success"
        # 后面要讨论下，是不是脚本先忽略
        elif file "$(realpath "$test_command")" 2>&1 | grep -w "script" | grep -qw "text"; then
            msg "MMM $index command: $test_command is script, ignore"
        else
            msg "NNN $index command: $test_command execute fail"
            test_flag_ce=$((test_flag_ce + 1))
        fi
    done

    cd "$cwd" || return 1
    msg "test_flag_ce=$test_flag_ce"
    if [ $test_flag_ce -eq 0 ]; then
        return 0
    else
        return 1
    fi
}

################################################################################
# Name        : get_tlinux_ver()
# Description : 获得tlinux的版本
#
# Returns     : 版本id：1.2 or 2.2 or 2.4
# Usage       :
################################################################################
get_tlinux_ver() {
    # 检查tlinux的版本
    #version=`cat /etc/issue|head -1|awk '{print $4}'`
    version=$(grep -o "[[:digit:]]\{1,\}\.[[:digit:]]\{1,\}" /etc/tlinux-release)
    echo "$version"
    return
}

################################################################################
# Name        : is_kvm_vm()
# Description : 判断一个机器是否是kvm的机器
#
# Returns     : exit code (1 = True, 0 = False) 1表示是kvm机器 0表示不是kvm机器
# Usage       :
################################################################################
is_kvm_vm() {
    # 判断一个机器是否是kvm的机器
    # 1表示是kvm机器,对应cvm
    # 0表示不是kvm机器,对应裸金属
    local ret1
    local ret2
    # For ARM, ARM device not modelname
    archtype=$(lscpu | grep Architecture | awk '{print $2}')
    if [ "$archtype" == "aarch64" ]; then
        sys_vendor=$(cat /sys/class/dmi/id/sys_vendor)
        chassis_vendor=$(cat /sys/class/dmi/id/chassis_vendor)

        if [[ $sys_vendor == "Tencent Cloud" ]] || [[ $chassis_vendor == "QEMU" ]]; then
            echo 1
        else
            echo 0
        fi
    else
        lscpu | grep -i kvm >/dev/null 2>&1
        ret1=$?

        grep -i kvm /sys/devices/system/clocksource/clocksource0/available_clocksource >/dev/null 2>&1
        ret2=$?

        if [ "$ret1" == "0" ] || [ "$ret2" == "0" ]; then
            echo 1
        else
            echo 0
        fi

    fi

}

################################################################################
# Name        : strstr()
# Description : Search a specific string (or regular expression) in another
#
# Returns     : exit code (1 = True, 0 = False)
# Usage       : if strstr() "there is a needle in the haystack" "needle"; echo "Found"; else "Not found"; fi
################################################################################
strstr() {
    # 判断str1是否包含str2
    # 1表示包含
    # 0表示不包含
    if [[ $1 == *$2* ]]; then
        return 1
    fi
    return 0

}

################################################################################
# Name        : proc_output()
# Description : 按照固定格式，把当前的结果输出的文件
#
# Returns     :
# Usage       :
################################################################################
proc_output() {
    # 输出当前执行结果
    # 输出的格式是 检查的函数函数名字
    # 结果是：failed ignored  unsupported success
    # 例如: ping_check $output_success
    output_file='/tmp/check_output.txt'
    if [ ! -f "$output_file" ]; then
        touch "$output_file"
    else
        : >$output_file
    fi
    local result=$1
    echo ${FUNCNAME[1]}
    echo "${FUNCNAME[1]} $result" >>$output_file
}

################################################################################
# Name        : check_os_type()
# Description : 获得当前镜像的操作系统
#
# Returns     :
# Usage       :
################################################################################
check_os_type() {
    # 检查操作系统类型
    # ostype: tlinux|opensuse|suse|centos|redhat|ubuntu|debian|coreos
    while true; do
        if [ -f /etc/tlinux-release ]; then
            echo tlinux
            return
        fi
        if [ -f /etc/SuSE-release ]; then
            grep -i "opensuse" /etc/SuSE-release >/dev/null 2>/dev/null && echo "opensuse" || echo "sles"
            return
        fi
        if [ -f /etc/SUSE-brand ]; then
            grep -i "opensuse" /etc/SUSE-brand &>/dev/null && echo "slse" || echo "opensuse"
            return
        fi
        if [ -f /etc/centos-release ]; then
            echo centos
            return
        fi
        #centos5 and redhat5
        if [ -f /etc/redhat-release ]; then
            grep "Red Hat" /etc/redhat-release >/dev/null
            if [ $? -eq 0 ]; then
                echo redhat
                return
            fi
            grep CentOS /etc/redhat-release >/dev/null
            if [ $? -eq 0 ]; then
                echo centos
                return
            fi
        fi
        break
    done
    for os in ubuntu debian coreos; do
        grep ^ID=${os}$ /etc/os-release >/dev/null 2>/dev/null && echo ${os} && return
    done
    grep -i =ubuntu /etc/lsb-release >/dev/null 2>/dev/null && echo ubuntu && return
    [ -f /etc/freebsd-update.conf ] && echo FreeBSD
}

ostype=$(check_os_type)

################################################################################
# Name        : command_exist()
# Description : 判断一个命令是否存在
#
# Returns     : exit code (0 = True, 1 = False) 0表示存在 1表示不存
# Usage       :
################################################################################
command_exist() {
    # 判断一个命令是否存在
    # 通过判断返回值$?不是0则表示命令不存
    #  [ $? -ne 0 ]
    type $1 &>/dev/null
}

################################################################################
# Name        : create_temp_file()
# Description : Creates a temporary file
#
# Returns     : TEMP_FILE (variable)
# Usage       : create_temp_file
#               if [ ! "${TEMP_FILE}" = "" ]; then
#                   MYTMPFILE="${TEMP_FILE}"
#                   echo "My temporary file is ${MYTMPFILE}"
#               fi
################################################################################

create_temp_file() {
    TEMP_FILE=""

    TEMP_FILE=$(mktemp /tmp/check.XXXXXXXXXX) || exit 1

    if [ ! "${TEMP_FILE}" = "" ]; then
        echo "Action: created temporary file ${TEMP_FILE}"
    else
        echo "Could not create a temporary file"
    fi
    # Add temporary file to queue for cleanup later
    TEMP_FILES="${TEMP_FILES} ${TEMP_FILE}"
}

################################################################################
# Name        : file_is_readable()
# Description : Check if a file readable or directory is accessible
#
# Returns     : Return code (0 = readable, 1 = not readable)
# Usage       : if file_is_readable /etc/shadow; then echo "File is readable"; fi
################################################################################

file_is_readable() {
    if [ $# -eq 0 ]; then echo "Function file_is_readable() called without a file name"; fi
    sFILE=$1
    CANREAD=0
    RETVAL=1
    escaped_file=$(echo ${sFILE} | sed 's/\*/\\*/; s/?/\\?/')

    # Check for symlink
    if [ -L "${escaped_file}" ]; then
        echo "Function file_is_readable() called can not with a symlink name"
    fi

    # Only check the file if it isn't a symlink (after previous check)
    if [ -d "${escaped_file}" ]; then
        OTHERPERMS=$(${LSBINARY} -d -l "${escaped_file}" 2>/dev/null | ${CUTBINARY} -c 8)
    elif [ -f "${escaped_file}" ]; then
        OTHERPERMS=$(${LSBINARY} -d -l "${escaped_file}" 2>/dev/null | ${CUTBINARY} -c 8)
    else
        OTHERPERMS="-"
    fi

    # Also check if we are the actual owner of the file (use -d to get directory itself, if its a directory)
    FILEOWNER=$(ls -dln "${escaped_file}" 2>/dev/null | ${AWKBINARY} -F" " '{ print $3 }')
    if [ "${FILEOWNER}" = "${MYID}" ]; then
        if [ -d "${escaped_file}" ]; then
            OTHERPERMS=$(${LSBINARY} -d -l "${escaped_file}" 2>/dev/null | ${CUTBINARY} -c 2)
        elif [ -f "${escaped_file}" ]; then
            OTHERPERMS=$(${LSBINARY} -l "${escaped_file}" 2>/dev/null | ${CUTBINARY} -c 2)
        fi
    else
        echo "Result: file is not owned by current user ID (${MYID}), but UID ${FILEOWNER}"
    fi

    # Check if we are root, or have the read bit
    if [ "${MYID}" = "0" ] || [ "${OTHERPERMS}" = "r" ]; then
        CANREAD=1
        return 0
    else
        return 1
    fi
}

################################################################################
# Name        : is_running()
# Description : Check if a process is running
#
# Parameters  : $1 = search argument
#               $2 = optional arguments
# Returns     : 0 (process is running), 1 (process not running)
#               RUNNING (1 = running, 0 = not running) - will be deprecated
# Notes       : PSOPTIONS are declared globally, to prevent testing each call
################################################################################

is_running() {
    if [ $# -eq 0 ]; then echo "Missing parameter when calling IsRunning function"; fi
    pgrep_options="-x"
    search=""
    FIND=""
    PSOPTIONS=""
    PARTIAL_SEARCH=1

    while [ $# -ge 1 ]; do
        case $1 in
            --full)
                pgrep_options="-f" # replace -x with -f
                PARTIAL_SEARCH=0
                ;;
            --user)
                shift
                users="$1"
                ;;
            *)
                search="$1"
                ;;
        esac
        shift # Go to next parameter
    done

    if [ -z "${search}" ]; then echo "Missing process to search for when using IsRunning function"; fi

    if [ -n "${users}" ]; then
        for u in ${users}; do
            user_uid=$(getent passwd "${u}" 2>/dev/null | ${AWKBINARY} -F: '{print $3}')
            # Only perform search if user exists and we had no match yet
            if [ -n "${user_uid}" ]; then
                if [ -z "${FIND}" ]; then
                    if [ ${PARTIAL_SEARCH} -eq 1 ]; then
                        echo "Performing ps scan using partial match and for uid ${user_uid}"
                        FIND=$(${PSBINARY} -u "${user_uid}" -o comm= "${search}" |
                            ${AWKBINARY} -v pattern="${search}" '$0 ~ pattern {print}')
                    else
                        LogText "Performing ps scan using exact match and for uid ${user_uid}"
                        FIND=$(${PSBINARY} -u "${user_uid}" -o comm= "${search}" |
                            ${AWKBINARY} -v pattern="^${search}$" '$0 ~ pattern {print}')
                    fi
                fi
            fi
        done
    else

        PSOPTIONS=" -o args= -C ${search}"

        if [ ${PARTIAL_SEARCH} -eq 1 ]; then
            echo "Performing ps scan using partial match and without uid"
            FIND=$(${PSBINARY} ${PSOPTIONS} | ${AWKBINARY} -v pattern="${search}" '$0 ~ pattern {print}')
        else
            echo "Performing ps scan using exact match and without uid"
            FIND=$(${PSBINARY} ${PSOPTIONS} | ${AWKBINARY} -v pattern="^${search}$" '$0 ~ pattern {print}')
        fi
    fi

    if [ -n "${FIND}" ]; then
        RUNNING=1
        echo "IsRunning: process '${search}' found (${FIND})"
        return 0
    else
        echo "IsRunning: process '${search}' not found"
        return 1
    fi
}

################################################################################
# Name        : get_rc_local_file()
# Description : 获取镜像rc.local文件路径
#
# Returns     : rc.local绝对路径
# Usage       :
################################################################################
get_rc_local_file() {
    if [ "${ostype}" = 'ubuntu' ] || [ "${ostype}" = 'debian' ] || [ "${ostype}" = 'FreeBSD' ]; then
        file=/etc/rc.local
    else
        file=/etc/rc.d/rc.local
    fi
    echo "$file"
}

################################################################################
# Name        : check_file_permissions()
# Description : Check file permissions
#
# Parameters  : Full path to file or directory
# Returns     : PERMS (FILE_NOT_FOUND -1 | OK 0 | BAD 1)
# Notes       : This function might be replaced in future
################################################################################
check_file_permissions() {
    #CHECKFILE="$1"
    CHECKFILE=$(echo "$1" | cut -d: -f1)
    PROFILEVALUE=$(echo "$1" | cut -d: -f2)
    if [ ! -d "${CHECKFILE}" ] && [ ! -f "${CHECKFILE}" ]; then
        PERMS="FILE_NOT_FOUND"
        return 1
        FILEVALUE=""
    else
        # If 'file' is an directory, use -d
        if [ -d ${CHECKFILE} ]; then
            FILEVALUE=$(ls -d -l ${CHECKFILE} | cut -c 2-10)
            #PROFILEVALUE=$(grep '^permdir' ${PROFILE} | grep "=${CHECKFILE}:" | cut -d: -f2)
        else
            FILEVALUE=$(ls -l ${CHECKFILE} | cut -c 2-10)
            #PROFILEVALUE=$(grep '^permfile' ${PROFILE} | grep "=${CHECKFILE}:" | cut -d: -f2)
        fi
        if [ "${FILEVALUE}" = "${PROFILEVALUE}" ]; then
            PERMS="OK"
            return 0
        else
            PERMS="BAD"
            return 1
        fi
    fi
}

# 先安装软件包，然后再测试软件包里面的命令，再测该包的服务
# $1 -- 临时目录
# $2 -- 软件包列表
# $3 -- 桌面相关软件包白名单列表
# $4 -- 服务相关软件包白名单列表
test_package_command() {
    msg "test_package_command $*"
    mkdir -p "$1"
    local pkg_white_list="$3"
    #local service_white_list="$4"
    local cwd
    cwd="$(pwd)"
    local tmpdir
    tmpdir="$(realpath "$1")"
    local pkg_list_all="$2"   
    #排除debuginfo包、debugsource包和ix86包
    msg "CLEAN: clean package list"
    grep "livepatch-kernel-\|\-debuginfo-\|\-debugsource-\|\.i[0-9]86$" $pkg_list_all
    msg "TEST: package list to test"
    grep -v "livepatch-kernel-\|\-debuginfo-\|\-debugsource-\|\.i[0-9]86$" $pkg_list_all | tee "${tmpdir}/pkg.list" >/dev/null
    local pkg_list="${tmpdir}/pkg.list"
    local repo=""
    local test_flag_tp=0
    local nr_list
    local arch
    arch="$(uname -m)"
    local install_index=0
    nr_list="$(wc -l <"$pkg_list")"
    msg "line of pkg_list is $nr_list"
    # shellcheck disable=SC2086
    rpm -qa >"${tmpdir}/installed.list"

    if [ "$nr_list" -eq 0 ]; then
        msg "no package to test"
        return 0
    fi

    local install_index=0
    local log_file

    local installed_list="$tmpdir/installed.rpm.list"
    rpm -qa | sort >"$installed_list"
    local tested_list="$tmpdir/tested.list"
    # 已经安装的软件包，不进行测试
    cp "$installed_list" "$tested_list"

    # local repo_whitelist=$(find /etc/yum.repos.d/ -type f -name "*.repo")
    mkdir -p "$tmpdir/workdir"
    cd "$tmpdir/workdir" || return 1

    local result_file
    result_file="${cwd}/package-command-result-$(date '+%Y%m%d-%H%M%S').txt"
    msg "result: $result_file"
    rm -f "$result_file"
    echo -e "# source-package\tbinary_package\tinstall\tcommand\tremove" >"$result_file"
    while [ $install_index -lt "$nr_list" ]; do
        install_index=$((install_index + 1))
        local pkg_name
        local pkg_src
        pkg_name="$(sed -n "${install_index}p" "$pkg_list" | awk '{print $1}')"
        msg "${install_index} / ${nr_list} : $pkg_name"
        [ -z "$pkg_name" ] && continue
        # 检查软件包是否安装成功
        log_file="$tmpdir/log.$pkg_name"
        if echo $pkg_name | grep -q "module+" || echo $pkg_name | grep -q "module_"; then
            msg "module package test!"
            #该包是module包，需要特殊处理
            module_pkg=$(dnf module provides $pkg_name |grep "^Module" | uniq | awk '{print $3}')
            if [ -z $module_pkg ]; then
                msg "$pkg_name not enable"
                continue
            fi
            msg "$module_pkg need enable!"
            # enable该module包所需主包的版本
            if dnf module enable -y $module_pkg; then
                msg "$module_pkg enable success"
                if dnf install -y "$pkg_name" >"${log_file}.install" 2>&1; then
                    msg "yum install module package $pkg_name install success"
                    pkg_src="$(get_src_pkg_by_bin "$pkg_name")"
                    echo -ne "${pkg_src}\t${pkg_name}\tPASS" >>"$result_file"
                else
                    msg "yum install module package $pkg_name install fail"
                    pkg_src="$(get_src_pkg_by_bin "$pkg_name")"
                    echo -e "${pkg_src}\t${pkg_name}\tFAIL\tSKIP\tSKIP" >>"$result_file"
                    #yum list $pkg_name
                    test_flag_tp=$((test_flag_tp + 1))
                    cat "${log_file}.install"
                    continue
                fi
            else
                # 如果enable主包失败，需要先将当前主包reset，再enable所需的版本
                module_pkg_main=$(echo ${module_pkg%%:*})
                msg "$module_pkg_main need tu reset!"
                if dnf module reset -y $module_pkg_main; then
                    if dnf module enable -y $module_pkg; then
                        # enable主包后，需要yum list，如果yum list失败，则说明repodata未更新，需要报错
                        # if ! yum list "$pkg_name"; then
                        #     msg "yum list $pkg_name fail, please check repodata!"
                        #     #yum list $pkg_name
                        #     test_flag_tp=$((test_flag_tp + 1))
                        # fi
                        if dnf install -y "$pkg_name" >"${log_file}.install" 2>&1; then
                            msg "yum install module package $pkg_name success"
                            pkg_src="$(get_src_pkg_by_bin "$pkg_name")"
                            echo -ne "${pkg_src}\t${pkg_name}\tPASS" >>"$result_file"
                        else
                            msg "yum install module package $pkg_name install fail"
                            pkg_src="$(get_src_pkg_by_bin "$pkg_name")"
                            echo -e "${pkg_src}\t${pkg_name}\tFAIL\tSKIP\tSKIP" >>"$result_file"
                            #yum list $pkg_name
                            test_flag_tp=$((test_flag_tp + 1))
                            cat "${log_file}.install"
                            continue
                        fi                   
                    else
                        msg "dnf module enable $module_pkg fail"
                        msg "yum install module package $pkg_name install fail"
                        pkg_src="$(get_src_pkg_by_bin "$pkg_name")"
                        echo -e "${pkg_src}\t${pkg_name}\tFAIL\tSKIP\tSKIP" >>"$result_file"
                        #yum list $pkg_name
                        test_flag_tp=$((test_flag_tp + 1))
                        continue
                    fi
                else
                    msg "dnf module reset -y $module_pkg_main fail"
                    msg "yum install module package $pkg_name install fail"
                    pkg_src="$(get_src_pkg_by_bin "$pkg_name")"
                    echo -e "${pkg_src}\t${pkg_name}\tFAIL\tSKIP\tSKIP" >>"$result_file"
                    #yum list $pkg_name
                    test_flag_tp=$((test_flag_tp + 1))
                    continue
                fi
            fi
        else
            msg "normal package test!"
            # 该包非module包，如果yum源中有该包，但是yum list失败，则说明repodata未更新，需要报错
            # if ! yum list "$pkg_name"; then
            #     msg "yum list $pkg_name fail, please check repodata!"
            #     #yum list $pkg_name
            #     test_flag_tp=$((test_flag_tp + 1))
            # fi
            #该包是普通包，正常安装即可
            if test_single_rpm_install "$repo" "$pkg_name" "$log_file"; then
                msg "yum install $pkg_name success"
                pkg_src="$(get_src_pkg_by_bin "$pkg_name")"
                echo -ne "${pkg_src}\t${pkg_name}\tPASS" >>"$result_file"
            else
                msg "yum install $pkg_name install fail"
                pkg_src="$(get_src_pkg_by_bin "$pkg_name")"
                echo -e "${pkg_src}\t${pkg_name}\tFAIL\tSKIP\tSKIP" >>"$result_file"
                #yum list $pkg_name
                test_flag_tp=$((test_flag_tp + 1))
                continue
            fi
        fi

        # 将该软件包命令找出来进行测试，如果是ts4或oc9，不测试命令
        if [ -s /etc/yum/vars/releasever ] && grep -q "^4\|^9" /etc/yum/vars/releasever; then
            echo "version is ts4 or oc9, do not test command"
        else
            temp_pkg=$(yum list  $pkg_name | tail -n 1 | awk '{print $1}' |sed 's|\..*||g')
            echo "package $pkg_name real name is $temp_pkg"
            if ! grep "^$temp_pkg$" $pkg_white_list; then
                # 找到elf文件，临时排除掉nde-相关包命令的测试
                rpm -ql "$pkg_name" | grep "/usr/bin/\|/usr/sbin/" | grep -v "\.so\|/usr/bin/nde-" >"$tmpdir/${install_index}.test.file.list"
                touch "$tmpdir/${install_index}.test.command.list"
                get_elf_executable "$tmpdir/${install_index}.test.file.list" "$tmpdir/${install_index}.test.command.list"
                if test_command_execute "$tmpdir/${install_index}/logs" "$tmpdir/${install_index}.test.command.list"; then
                    msg "all command for $pkg_name test success"
                    echo -ne "\tPASS" >>"$result_file"
                else
                    msg "command for $pkg_name test fail"
                    echo -ne "\tFAIL" >>"$result_file"
                    #yum list $pkg_name
                    test_flag_tp=$((test_flag_tp + 1))
                fi
            fi
        fi
        # 将该软件包的服务找出来测试
        # if ! grep "^$temp_pkg" $service_white_list; then
        #     rpm -ql "$pkg_name" | grep "^/usr/lib/systemd/system/.*.service$" | grep -v "@" | grep -v "systemd-.*.service\|initrd.*.service\|anaconda.*.service\|coreos.*.service" | awk -F/ '{print $NF}'  >"$tmpdir/${install_index}.service.file.list"
        #     if [ -s "$tmpdir/${install_index}.service.file.list" ]; then
        #         msg "$pkg_name has service, need to test"
        #         while read -r line; do
        #             msg "$line test!"
        #             if timeout -s SIGKILL 30 systemctl --no-pager start $line >"${tmpdir}/$line" 2>&1; then
        #                 msg "$line start success"
        #             else
        #                 systemctl --no-pager status $line
        #                 msg "$line from $pkg_name start fail"
        #                 yum list $pkg_name
        #                 test_flag_tp=$((test_flag_tp + 1))
        #             fi
        #             if timeout -s SIGKILL 30 systemctl --no-pager stop $line >"${tmpdir}/$line" 2>&1; then
        #                 msg "$line stop success"
        #             else
        #                 if grep -iqw "it is configured to refuse manual start/stop" "${tmpdir}/$line"; then
        #                     msg "$line stop success"
        #                 else              
        #                     systemctl --no-pager status $line
        #                     msg "$line from $pkg_name stop fail"
        #                     yum list $pkg_name
        #                     test_flag_tp=$((test_flag_tp + 1))
        #                 fi
        #             fi
        #             if timeout -s SIGKILL 30 systemctl --no-pager restart $line >"${tmpdir}/$line" 2>&1; then
        #                 msg "$line restart success"
        #             else
        #                 if grep -iqw "it is configured to refuse manual start/stop" "${tmpdir}/$line"; then
        #                     msg "$line restart success"
        #                 else
        #                     systemctl --no-pager status $line
        #                     msg "$line from $pkg_name restart fail"
        #                     yum list $pkg_name
        #                     test_flag_tp=$((test_flag_tp + 1))
        #                 fi
        #             fi
        #         done < "$tmpdir/${install_index}.service.file.list"
        #     else
        #         msg "$pkg_name has no service"
        #     fi
        # fi

        # 检查软件包是否移除成功
        if ! grep "^$pkg_name" "${tmpdir}/installed.list"; then
            if yum remove -y "$pkg_name" >"${log_file}.remove" 2>&1; then
                if grep "scriptlet failed" "${log_file}.remove"; then
                    cat "${log_file}.remove"
                    msg "yum remove $pkg_name remove fail"
                    echo -e "\tFAIL" >>"$result_file"
                    yum list $pkg_name
                    test_flag_tp=$((test_flag_tp + 1))
                else
                    msg "yum remove $pkg_name success"
                    echo -e "\tPASS" >>"$result_file"
                fi
            else
                if grep -iqw "The operation would result in removing the following protected packages" "${log_file}.remove"; then
                    msg "package $pkg_name is protected, can't be removed"
                    echo -e "\tPASS" >>"$result_file"
                else
                    cat "${log_file}.remove"
                    msg "yum remove $pkg_name remove fail"
                    echo -e "\tFAIL" >>"$result_file"
                    yum list $pkg_name
                    test_flag_tp=$((test_flag_tp + 1))
                fi
            fi
        fi

        # # 若当前测试的软件包是与release相关的包，则需要disable其遗留下来的repo
        # if [[ "$pkg_name" == *"release"* ]]; then
        #     disable_non_whitelisted_repos "$repo_whitelist"
        # fi
    done

    msg "result: $result_file"
    cd "$cwd" || return 1
    msg "test_flag_tp=$test_flag_tp"
    if [ $test_flag_tp -eq 0 ]; then
        return 0
    else
        return 1
    fi
}

rpm_service_get_list() {
    systemctl --no-pager list-units --type=service | grep -v "UNIT.*LOAD" | sed "/^$/,$ d" |
        sed "s|^[[:blank:]]*●*[[:blank:]]*||g" | awk '{print $1}' |
        grep -vw "run-[a-z0-9]\{33\}\.service\|man-db-cache-update.service" | sort
}

# 只遍历有服务的rpm包，对其进行服务的启停和重启测试
test_rpm_service_simple() {
    msg "test_rpm_service $*"
    mkdir -p "$1"

    local cwd
    cwd="$(pwd)"
    local tmpdir
    tmpdir="$(realpath "$1")"
    local pkg_list="$2"
    # i386/586/686的包不需要安装，在此排除掉这些包
    cat $pkg_list | grep -v ".*.i386" | grep -v ".*.i586" | grep -v ".*.i686" >"${tmpdir}/pkg_without_ix86.list"
    local nr_list
    nr_list="$(wc -l <"${tmpdir}/pkg_without_ix86.list")"
    local test_flag_rss=0
    # 获取初始系统正在运行，类型为service的Unit
    rpm_service_get_list >"${tmpdir}/default_initial.service"

    while read -r line; do
        install_index=$((install_index + 1))
        msg "${install_index} / ${nr_list} : $line"

        local package_name
        package_name="$(echo "$line" | awk '{print $1}')"
        [ -z "$package_name" ] && continue

        # 检查软件包服务是否安装成功
        log_file="$tmpdir/log.${package_name}"
        if test_single_rpm_install "$repo" "$package_name" "$log_file"; then
            msg "yum install $package_name success"
        else
            msg "yum install $package_name fail"
            test_flag_rss=$((test_flag_rss + 1))
            continue
        fi

        # 和安装前的服务列表进行对比，找到新增的服务和减少的服务（减少的服务可能是有问题，要找出来人工确认）
        # 安装软件包之后的service列表
        rpm_service_get_list >"${tmpdir}/added_$package_name.service"

        # 如果有新增服务，则测试服务的启停和重启
        # 遍历新增的服务，确认是否可以正常启停
        diff "${tmpdir}/default_initial.service" "${tmpdir}/added_$package_name.service" >"${tmpdir}/temp_diff_$package_name.service"
        if [ -s "${tmpdir}/temp_diff_$package_name.service" ]; then
            if grep "^< " "${tmpdir}/temp_diff_$package_name.service"; then
                msg "some service removed by package $package_name"
                test_flag_rss=$((test_flag_rss + 1))
            fi
            grep "^> " "${tmpdir}/temp_diff_$package_name.service" | awk '{print $2}' | while read -r s; do
                msg "test service $s in package $package_name"
                # 将有服务的rpm包名追加到服务列表中
                if timeout -s SIGKILL 10 systemctl --no-pager start $s >"${log_file}.service" 2>&1; then
                    msg "$s of $package_name start success"
                else
                    msg "$s of $package_name start fail"
                    test_flag_rss=$((test_flag_rss + 1))
                fi
                if timeout -s SIGKILL 10 systemctl --no-pager stop $s >"${log_file}.service" 2>&1; then
                    msg "$s of $package_name stop success"
                else
                    msg "$s of $package_name stop fail"
                    test_flag_rss=$((test_flag_rss + 1))
                fi
                if timeout -s SIGKILL 10 systemctl --no-pager restart $s >"${log_file}.service" 2>&1; then
                    msg "$s of $package_name restart success"
                else
                    msg "$s of $package_name restart fail"
                    test_flag_rss=$((test_flag_rss + 1))
                fi
            done
        else
            msg "package $package_name doesn't have service"
        fi

        # 软件包是本用例测试时安装的，需要移除
        if yum remove -y "$package_name" >"${log_file}.remove" 2>&1; then
            msg "yum remove $package_name success"
        else
            if grep -iqw "The operation would result in removing the following protected packages" "${log_file}.remove"; then
                msg "package $package_name is protected, cann't be removed"
            else
                msg "yum remove $package_name fail"
                test_flag_rss=$((test_flag_rss + 1))
                cat "${log_file}.remove"
            fi
        fi

        # 卸载软件包，和安装软件包前进行对比，软件包卸载前后服务不应该有变化
        rpm_service_get_list >"${tmpdir}/remove_$package_name.service"
        if ! diff "${tmpdir}/default_initial.service" "${tmpdir}/remove_$package_name.service" >"${tmpdir}/diff_before_and_after_install_$package_name.service"; then
            msg "the service changed after package $package_name remove"
            test_flag_rss=$((test_flag_rss + 1))
        fi
        # 将服务列表再备份一次，用于下一个循环
        rpm_service_get_list >"${tmpdir}/default_initial.service"
    done <"${tmpdir}/pkg_without_ix86.list"

    cd "$cwd" || return 1
    msg "test_flag_rss=$test_flag_rss"
    if [ $test_flag_rss -eq 0 ]; then
        return 0
    else
        return 1
    fi
}

# 测试软件包服务启停
# $1 -- 测试用的临时目录
# $2 -- 要安装的软件包列表，yum list输出的即可
# $3 -- 同yum的--repo，可选参数，有的软件在指定repo中
test_rpm_service() {
    msg "test_rpm_service $*"
    mkdir -p "$1"

    local cwd
    cwd="$(pwd)"
    local tmpdir
    tmpdir="$(realpath "$1")"
    local pkg_list="$2"
    # i386/586/686的包不需要安装，在此排除掉这些包
    grep -v ".*\.i[0-9]86" "$pkg_list" >"${tmpdir}/pkg_without_ix86.list"
    local repo="$3"
    [ -n "$repo" ] && repo="--enablerepo=$repo"
    local nr_list
    local arch
    arch="$(uname -m)"
    nr_list="$(wc -l <"${tmpdir}/pkg_without_ix86.list")"
    local install_index=0
    local log_file
    local fail_service_num
    local test_flag_rs=0
    local package_has_service_num=0
    local service_list
    service_list=$(find / -name package_has_service.list)

    mkdir -p "$tmpdir/workdir"
    cd "$tmpdir/workdir" || return 1

    yum $repo list --installed >"${tmpdir}/installed.list"
    sed -i "0,/Installed Packages/d" "${tmpdir}/installed.list"

    if systemctl --no-pager list-units --type=service --state failed; then
        msg "installed package's service fail by default"
    fi

    # 获取初始系统正在运行，类型为service的Unit
    rpm_service_get_list >"${tmpdir}/default_initial.service"
    # 清空有服务的rpm包的列表
    sed -i '1,$d' $service_list
    # 遍历软件包
    while read -r line; do
        install_index=$((install_index + 1))
        msg "${install_index} / ${nr_list} : $line"

        local package_name
        package_name="$(echo "$line" | awk '{print $1}')"
        [ -z "$package_name" ] && continue

        # 检查软件包服务是否安装成功
        log_file="$tmpdir/log.${package_name}"
        if test_single_rpm_install "$repo" "$package_name" "$log_file"; then
            msg "yum install $package_name success"
        else
            msg "yum install $package_name fail"
            test_flag_rs=$((test_flag_rs + 1))
            continue
        fi

        # 和安装前的服务列表进行对比，找到新增的服务和减少的服务（减少的服务可能是有问题，要找出来人工确认）
        # 安装软件包之后的service列表
        rpm_service_get_list >"${tmpdir}/added_$package_name.service"

        # 如果有新增服务，则测试服务的启停和重启
        # 遍历新增的服务，确认是否可以正常启停
        diff "${tmpdir}/default_initial.service" "${tmpdir}/added_$package_name.service" >"${tmpdir}/temp_diff_$package_name.service"
        if [ -s "${tmpdir}/temp_diff_$package_name.service" ]; then
            if grep "^< " "${tmpdir}/temp_diff_$package_name.service"; then
                msg "some service removed by package $package_name"
                test_flag_rs=$((test_flag_rs + 1))
            fi
            for s in $(grep "^> " "${tmpdir}/temp_diff_$package_name.service" | awk '{print $2}'); do
                msg "test service $s in package $package_name"
                # 将有服务的rpm包名追加到服务列表中
                echo "$package_name" >>$service_list
                if timeout -s SIGKILL 10 systemctl --no-pager start $s >"${log_file}.service" 2>&1; then
                    msg "$s of $package_name start success"
                else
                    msg "$s of $package_name start fail"
                    test_flag_rs=$((test_flag_rs + 1))
                fi
                if timeout -s SIGKILL 10 systemctl --no-pager stop $s >"${log_file}.service" 2>&1; then
                    msg "$s of $package_name stop success"
                else
                    msg "$s of $package_name stop fail"
                    test_flag_rs=$((test_flag_rs + 1))
                fi
                if timeout -s SIGKILL 10 systemctl --no-pager restart $s >"${log_file}.service" 2>&1; then
                    msg "$s of $package_name restart success"
                else
                    msg "$s of $package_name restart fail"
                    test_flag_rs=$((test_flag_rs + 1))
                fi
            done
        else
            msg "package $package_name doesn't have service"
        fi

        # 如果软件包原来就装了，本用例不remove
        if grep -q "^${package_name}[[:blank:]]" "${tmpdir}/installed.list" ||
            grep -q "^${package_name}\.\(${arch}\|noarch\)[[:blank:]]" "${tmpdir}/installed.list"; then
            # 软件包原本就已安装，不移除
            msg "package $package_name is installed by default, don't remove"
        else
            # 软件包是本用例测试时安装的，需要移除
            if yum $repo remove -y "$package_name" >"${log_file}.remove" 2>&1; then
                msg "yum remove $package_name success"
            else
                if grep -iqw "The operation would result in removing the following protected packages" "${log_file}.remove"; then
                    msg "package $package_name is protected, cann't be removed"
                else
                    msg "yum remove $package_name fail"
                    test_flag_rs=$((test_flag_rs + 1))
                    cat "${log_file}.remove"
                fi
            fi
        fi

        # 卸载软件包，和安装软件包前进行对比，软件包卸载前后服务不应该有变化
        rpm_service_get_list >"${tmpdir}/remove_$package_name.service"
        if ! diff "${tmpdir}/default_initial.service" "${tmpdir}/remove_$package_name.service" >"${tmpdir}/diff_before_and_after_install_$package_name.service"; then
            msg "the service changed after package $package_name remove"
            test_flag_rs=$((test_flag_rs + 1))
        fi
        # 将服务列表再备份一次，用于下一个循环
        rpm_service_get_list >"${tmpdir}/default_initial.service"
    done <"${tmpdir}/pkg_without_ix86.list"

    cd "$cwd" || return 1
    msg "test_flag_rs=$test_flag_rs"
    msg "package_has_service_num=$package_has_service_num"
    if [ $test_flag_rs -eq 0 ]; then
        return 0
    else
        return 1
    fi
}

# 测试默认软件包服务启停
test_default_rpm_service() {
    msg "test_default_rpm_service $*"
    local test_flag_drs=0

    # 获取初始系统正在运行，类型为service的Unit
    rpm_service_get_list >"${tmpdir}/default_initial.service"
    # 遍历这些服务，对每一个服务做启停和重启操作
    for line in $(cat "${tmpdir}/default_initial.service"); do
        if timeout -s SIGKILL 10 systemctl --no-pager start $line >"${tmpdir}/$line" 2>&1; then
            msg "$line start success"
        else
            msg "$line start fail"
            test_flag_drs=$((test_flag_drs + 1))
        fi
        if timeout -s SIGKILL 10 systemctl --no-pager stop $line >"${tmpdir}/$line" 2>&1; then
            msg "$line stop success"
        else
            if grep -iqw "it is configured to refuse manual start/stop" "${tmpdir}/$line"; then
                msg "$line stop success"
            else
                msg "$line stop fail"
                test_flag_drs=$((test_flag_drs + 1))
            fi
        fi
        if timeout -s SIGKILL 10 systemctl --no-pager restart $line >"${tmpdir}/$line" 2>&1; then
            msg "$line restart success"
        else
            if grep -iqw "it is configured to refuse manual start/stop" "${tmpdir}/$line"; then
                msg "$line restart success"
            else
                msg "$line restart fail"
                test_flag_drs=$((test_flag_drs + 1))
            fi
        fi
    done

    cd "$cwd" || return 1
    msg "test_flag_drs=$test_flag_drs"
    if [ $test_flag_drs -eq 0 ]; then
        return 0
    else
        return 1
    fi
}
